#ifndef FRAME_H
#define FRAME_H


#include "vect.h"
#include "matrix44.h"
#include <gl/gl.h>
#include <gl/glu.h>

//! Klasa enkapsuluje lokalny uklad wspolrzednych (lok) (orientacje w przestrzeni)
class Frame
{
 //klasa frame pracuje na wektorach i macierzach - wygodne typedef'y
 typedef Vect< float > Vector3D; 
 typedef Vector3D V3;
 typedef Matrix44< float > M44;

 protected:

     Vector3D origin;	// Where am I?
     Vector3D forward;	// Where am I going? os Z
     Vector3D up;		// Which way is up? os Y

	 float scaleX;		// skala na osi x
	 float scaleY;		// skala na osi y
	 float scaleZ;		// skala na osi Z

 public:

	Frame() : origin(0,0,0), forward(0,0,-1), up(0,1,0), scaleX( 1.0f ), scaleY( 1.0f ), scaleZ( 1.0f )
	{}

	inline void SetOrigin( const Vector3D origin_ ) 
	{ origin = origin_; }
    
    inline void SetOrigin( float x, float y, float z )
	{ origin.set( 1, x ); origin.set( 2, y ); origin.set( 3, z ); }

	inline Vector3D GetOrigin( )
	{ return origin; }

	inline float GetOriginX(void) { return origin.get(1); }
	inline float GetOriginY(void) { return origin.get(2); } 
	inline float GetOriginZ(void) { return origin.get(3); }

	//! Ustawia os Z
    inline void SetForwardVector(const Vector3D direction) 
	{ forward = direction; }

	//! Ustawia os Z
    inline void SetForwardVector(float x, float y, float z)
    { forward.set( 1, x ); forward.set( 2, y ); forward.set( 3, z ); }

	//! Pobiera os Z
    inline Vector3D GetForwardVector()
	{ return forward; }

	//! Ustawia os Y
    inline void SetUpVector(const Vector3D direction) 
	{ up = direction; }

	//! Ustawia os Y
    inline void SetUpVector(float x, float y, float z)
	{ up.set( 1, x ); up.set( 2, y ); up.set( 3, z ); }

	//! Ustawia os Y
    inline Vector3D GetUpVector() 
	{ return up; }

	//! Ustawia skale na osi x
	void SetScaleX( float s )
	{ scaleX = s; }
	//! Pobiera skale na osi x
	const float GetScaleX() const 
	{ return scaleX; }

	//! Ustawia skale na osi z
	void SetScaleZ( float s )
	{ scaleZ = s; }
	//! Pobiera skale na osi z
	const float GetScaleZ() const 
	{ return scaleZ; }

	//! Ustawia skale na osi y
	void SetScaleY( float s )
	{ scaleY = s; }
	//! Pobiera skale na osi y
	const float GetScaleY() const 
	{ return scaleY; }

	//! Ustawia skale na wszystkich osiach. s1 skala na x, s2 skala na y, s3 skala na z 
	void SetScale( float s1, float s2, float s3 )
	{ scaleX = s1; scaleY = s2; scaleZ = s3; }
	//! Pobiera skale na wszytstkich osiach. s[0] skala na x, s[1] skala na y, s[2] skala na z
	void GetScale( float* s  ) const 
	{ s[0] = scaleX; s[1] = scaleY; s[2] = scaleZ; }


	inline Vector3D GetZAxis() { return forward; }
	inline Vector3D GetYAxis() { return up; }

	//! Os X obliczana jest jako crossProduct osi up (y) oraz forward (z). Jesli z=(0,0,1) oraz y=(0,1,0) to x=(1,0,0)
	inline Vector3D GetXAxis() { return crossProduct( up, forward ); }

	inline void TranslateAlongZ( float fDelta );
	inline void TranslateAlongY( float fDelta );
	inline void TranslateAlongX( float fDelta );
	inline void TranslateAlongArbitrary( Vector3D t, float fDelta = 1 );
	inline void TranslateAlongArbitrary( const float x, const float y, const float z, float fDelta = 1 );

	//! Zwraca macierz tego lok
	M44 GetMatrix( bool rotationOnly );

	//! Funkcja wymnaza obecna macierz modelview przez macierz zadana tym ukladem wspolrzednych
	void ApplyActorTransform( bool rotationOnly = false );

	/*Obrot lok wzgledem osi x*/
	void RotateLocalX( float angle );

	/*Obrot lok wzgledem osi y*/
	void RotateLocalY( float angle );

	/*Obrot lok wzgledem osi Z*/
	void RotateLocalZ( float angle );

	/*Ten uklad taki sam jak inny*/
	void operator=( Frame& t );

	//! Normalizuje ten uklad wspolrzednych
	/*!
		F-cja nie bedzie dzialac jesli wektory up i forward sa rownolegle.
	*/
	void normalize();

	//! Zwraca wartosc abs(dotProduct(up, up)-1) + abs(dotProduct(forward, forward)-1)
	float getLengthError();

	//! Zwraca wartosc abs(dotProduct( up, forward ))
	float getPerpendicularError();

	//! Zwraca wartosc abs(dotProduct(up, up)-1) + abs(dotProduct(forward, forward)-1) + abs(dotProduct( up, forward ))
	float getError();
};

//----------------------------------------------- TranslateAlongZ ----------------------------------------------//
inline void Frame::TranslateAlongZ( float fDelta )
{
	origin = origin + (forward * fDelta);
}

//----------------------------------------------- TranslateAlongY ---------------------------------------------------//
inline void Frame::TranslateAlongY(float fDelta)
{
	origin = origin + (up* fDelta);
}

//------------------------------------------------ TranslateAlongX ----------------------------------------------//
inline void Frame::TranslateAlongX(float fDelta)
{
	origin = origin + GetXAxis() * fDelta;
}
//------------------------------------------------ TranslateAlongArbitrary ---------------------------------------//
inline void Frame::TranslateAlongArbitrary( const Vector3D t, float fDelta )
{
	origin = origin + t * fDelta;
}
//----------------------------------------------- TranslateAlongArbitrary -----------------------------------------//
inline void Frame::TranslateAlongArbitrary( const float x, const float y, const float z, float fDelta )
{
	origin = origin + Vector3D( x, y, z ) * fDelta;
}

#endif